#pragma once

#include <gtest/gtest.h>
#include "qlib/tests/db_test/check.h"
#include "qlib/tests/geo_test/random.h"

using namespace qlib;

gdstk::Library* gdstk_library;
Library::s_ptr qlib_library;

void resert_library() {
  char name[] = "Library";

  qlib_library = Library::create();
  qlib_library->name = name;
  qlib_library->unit = 1e-9;
  qlib_library->precision = 1e-9;

  gdstk_library = (gdstk::Library*)gdstk::allocate_clear(sizeof(gdstk::Library));
  gdstk_library->name = gdstk::copy_string(name, NULL);
  gdstk_library->unit = 1e-9;
  gdstk_library->precision = 1e-9;
}

TEST(PathTest1, BasicAssertion) {
  const char name[] = "My Cell";

  // gdstk example
  resert_library();
  gdstk::Cell* gdstk_cell = (gdstk::Cell*)gdstk::allocate_clear(sizeof(gdstk::Cell));
  {
    gdstk_cell->name = gdstk::copy_string(name, NULL);

    gdstk::FlexPath* fp = (gdstk::FlexPath*)gdstk::allocate_clear(sizeof(gdstk::FlexPath));
    fp->init(gdstk::Vec2{0, 0}, 1, 0.5, 0, 0.01, 0);
    fp->simple_path = true;

    gdstk::Vec2 points1[] = {{3, 0}, {3, 2}, {5, 3}, {3, 4}, {0, 4}};
    gdstk::Array<gdstk::Vec2> input_1;
    input_1.capacity = 0;
    input_1.count = COUNT(points1);
    input_1.items = points1;
    fp->segment(input_1, NULL, NULL, false);

    gdstk_cell->flexpath_array.append(fp);

    fp = (gdstk::FlexPath*)gdstk::allocate_clear(sizeof(gdstk::FlexPath));
    const double widths[] = {0.3, 0.2, 0.4};
    const double offsets[] = {-0.5, 0, 0.5};
    const Tag tags[] = {0, 0, 0};
    fp->init(gdstk::Vec2{12, 0}, 3, widths, offsets, 0.01, tags);

    fp->elements[0].end_type = gdstk::EndType::HalfWidth;
    fp->elements[0].join_type = gdstk::JoinType::Bevel;

    fp->elements[1].end_type = gdstk::EndType::Flush;
    fp->elements[1].join_type = gdstk::JoinType::Miter;

    fp->elements[2].end_type = gdstk::EndType::Round;
    fp->elements[2].join_type = gdstk::JoinType::Round;

    gdstk::Vec2 points2[] = {{8, 0}, {8, 3}, {10, 2}};
    gdstk::Array<gdstk::Vec2> input_2;
    input_2.capacity = 0;
    input_2.count = COUNT(points2);
    input_2.items = points2;
    fp->segment(input_2, NULL, NULL, false);

    fp->arc(2, 2, -M_PI / 2, M_PI / 2, 0, NULL, NULL);
    fp->arc(1, 1, M_PI / 2, 1.5 * M_PI, 0, NULL, NULL);

    gdstk_cell->flexpath_array.append(fp);

    gdstk_library->cell_array.append(gdstk_cell);

    tm time;
    gdstk::get_now(time);
    EXPECT_EQ((int)gdstk_library->write_gds("E:/T/gds/Path-tk-1.gds", 199, &time) == 0, true);

    // gdstk_cell->write_svg("E:/T/gds/Path-tk-1.svg", 10, 6, NULL, NULL, "#222222", 5, true, NULL);
  }

  // qlib example
  Cell::s_ptr qlib_cell = Cell::create();
  {
    qlib_cell->name = name;

    Path::s_ptr fp = Path::create();
    fp->init(Vec2d{0, 0}, 1, 0.5, 0, 0.01, 0, 0);
    fp->simple_path = true;

    Vec2dArray points1 = {{3, 0}, {3, 2}, {5, 3}, {3, 4}, {0, 4}};
    //Vec2d points1[] = {{3, 0}, {3, 2}, {5, 3}, {3, 4}, {0, 4}};
    fp->segment(points1, NULL, NULL, false);

    qlib_cell->paths.push_back(fp);

    fp = Path::create();
    const double widths[] = {0.3, 0.2, 0.4};
    const double offsets[] = {-0.5, 0, 0.5};
    const Tag tag = 0;
    fp->init(Vec2d{12, 0}, 3, widths, offsets, 0.01, get_layer(tag), get_type(tag));

    fp->elements[0].end_type = EndType::HalfWidth;
    fp->elements[0].join_type = JoinType::Bevel;

    fp->elements[1].end_type = EndType::Flush;
    fp->elements[1].join_type = JoinType::Miter;

    fp->elements[2].end_type = EndType::Round;
    fp->elements[2].join_type = JoinType::Round;

    Vec2dArray points2 = {{8, 0}, {8, 3}, {10, 2}};
    // Vec2d points2[] = {{8, 0}, {8, 3}, {10, 2}};
    fp->segment(points2, NULL, NULL, false);

    fp->arc(Vec2d(2, 2), -M_PI / 2, M_PI / 2, 0, NULL, NULL);
    fp->arc(Vec2d(1, 1), M_PI / 2, 1.5 * M_PI, 0, NULL, NULL);

    qlib_cell->paths.push_back(fp);

    qlib_library->cells.push_back(qlib_cell);

    tm time;
    gdstk::get_now(time);
    EXPECT_EQ((int)DB::write_gds(qlib_library, "E:/T/gds/Path-qlib-1.gds", 199, &time) == 0, true);
  }

  EXPECT_EQ(Check::check_library(qlib_library, gdstk_library), true);
}

TEST(PathTest2, BasicAssertion) {
  const char name[] = "My Cell";

  // gdstk example
  resert_library();
  gdstk::Cell* gdstk_cell = (gdstk::Cell*)gdstk::allocate_clear(sizeof(gdstk::Cell));
  {
    gdstk_cell->name = copy_string(name, NULL);

    gdstk::Vec2 points[] = {{0, 10}, {20, 0}, {18, 15}, {8, 15}};

    for (uint64_t i = 0; i < 2; i++) {
      gdstk::FlexPath* fp = (gdstk::FlexPath*)gdstk::allocate_clear(sizeof(gdstk::FlexPath));
      fp->init(gdstk::Vec2{0, 0}, 1, 0.5, 0, 0.01, 0);
      fp->simple_path = true;
      if (i == 0) {
        fp->elements[0].bend_type = gdstk::BendType::Circular;
        fp->elements[0].bend_radius = 5;
      } else {
        fp->elements[0].tag = make_tag(1, 0);
      }
      fp->segment({0, COUNT(points), points}, NULL, NULL, false);
      gdstk_cell->flexpath_array.append(fp);
    }

    gdstk_library->cell_array.append(gdstk_cell);

    tm time;
    gdstk::get_now(time);
    EXPECT_EQ((int)gdstk_library->write_gds("E:/T/gds/Path-tk-2.gds", 199, &time) == 0, true);
  }

  // qlib example
  Cell::s_ptr qlib_cell = Cell::create();
  {
    qlib_cell->name = name;

    Vec2dArray points = {{0, 10}, {20, 0}, {18, 15}, {8, 15}};

    for (uint64_t i = 0; i < 2; i++) {
      Path::s_ptr fp = Path::create();
      fp->init(Vec2d{0, 0}, 1, 0.5, 0, 0.01, 0, 0);
      fp->simple_path = true;
      if (i == 0) {
        fp->elements[0].bend_type = BendType::Circular;
        fp->elements[0].bend_radius = 5;
      } else {
        fp->elements[0].layer = 1;
        fp->elements[0].datatype = 0;
      }

      fp->segment(points, NULL, NULL, false);
      qlib_cell->paths.push_back(fp);
    }

    qlib_library->cells.push_back(qlib_cell);

    tm time;
    gdstk::get_now(time);
    EXPECT_EQ((int)DB::write_gds(qlib_library, "E:/T/gds/Path-qlib-2.gds", 199, &time) == 0, true);
  }

  EXPECT_EQ(Check::check_library(qlib_library, gdstk_library), true);
}

TEST(PathTest3, BasicAssertion) {
  const char name[] = "My Cell";

  // gdstk example
  resert_library();
  gdstk::Cell* gdstk_cell = (gdstk::Cell*)gdstk::allocate_clear(sizeof(gdstk::Cell));
  {
    gdstk_cell->name = copy_string(name, NULL);

    double widths[] = {0.5, 0.5};
    double offsets[] = {-0.5, 0.5};
    gdstk::Tag tags[] = {0, 0};
    gdstk::FlexPath* fp = (gdstk::FlexPath*)gdstk::allocate_clear(sizeof(gdstk::FlexPath));
    fp->init(gdstk::Vec2{0, 0}, 2, widths, offsets, 0.01, tags);

    fp->horizontal(2, NULL, NULL, false);

    widths[0] = 0.8;
    widths[1] = 0.8;
    offsets[0] = -0.9;
    offsets[1] = 0.9;
    fp->horizontal(4, widths, offsets, false);

    fp->horizontal(6, NULL, NULL, false);

    gdstk_cell->flexpath_array.append(fp);

    gdstk_library->cell_array.append(gdstk_cell);

    tm time;
    gdstk::get_now(time);
    EXPECT_EQ((int)gdstk_library->write_gds("E:/T/gds/Path-tk-3.gds", 199, &time) == 0, true);
  }

  // qlib example
  Cell::s_ptr qlib_cell = Cell::create();
  {
    qlib_cell->name = name;

    double widths[] = {0.5, 0.5};
    double offsets[] = {-0.5, 0.5};
    Tag tags[] = {0, 0};
    Path::s_ptr fp = Path::create();
    fp->init(Vec2d{0, 0}, 2, widths, offsets, 0.01, 0, 0);

    fp->horizontal(2, NULL, NULL, false);

    widths[0] = 0.8;
    widths[1] = 0.8;
    offsets[0] = -0.9;
    offsets[1] = 0.9;
    fp->horizontal(4, widths, offsets, false);

    fp->horizontal(6, NULL, NULL, false);

    qlib_cell->paths.push_back(fp);

    qlib_library->cells.push_back(qlib_cell);

    tm time;
    gdstk::get_now(time);
    EXPECT_EQ((int)DB::write_gds(qlib_library, "E:/T/gds/Path-qlib-3.gds", 199, &time) == 0, true);
  }

  EXPECT_EQ(Check::check_library(qlib_library, gdstk_library), true);
}